using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Reflection;
using CustomORM.DataAccess;
using CustomORM.Examples.CustomORM.vs.NH;
using CustomORM.Examples.Entities;
using CustomORM.Examples.Helpers;
using CustomORM.Mapping;

namespace CustomORM.Examples.Examples
{
    public class CormVsNhibernate
    {


        public static void Run()
        {
            // Select execution time

            CustomerRepository cormCustomerRepository = EstablishCormCustomerRepository();
            NhCustomerRepository nhCustomerRepository = EstablishNhCustomerRepository();

            var nhRes = MeasureExecTime.ExecTest(()=> nhCustomerRepository.FetchAllCustomers());
            var cormRes = MeasureExecTime.ExecTest(()=>cormCustomerRepository.FetchAllCustomers());

            Console.WriteLine(string.Format("Select all customers. NH time: {0} ms, CORM time: {1} ms", nhRes.EllapsedMilliseconds, cormRes.EllapsedMilliseconds));

            // Complete CRUD for one Customer

            RunCRUD_Nhibernate(nhCustomerRepository);

        }

        private static NhCustomerRepository EstablishNhCustomerRepository()
        {
            var settings = ConfigurationManager.ConnectionStrings["NorthwindConnectionString"];
            var sessionFactory = NhBootstrap.CreateSessionFactory(settings.ConnectionString, settings.ProviderName);
            return new NhCustomerRepository(sessionFactory);
        }

        private static CustomerRepository EstablishCormCustomerRepository()
        {
            MagicMapper.ScanForMappers(Assembly.GetExecutingAssembly());

            var settings = ConfigurationManager.ConnectionStrings["NorthwindConnectionString"];
            var ceDataAccess = new AdoDataAccess(settings.ConnectionString, settings.ProviderName);

            return new CustomerRepository(ceDataAccess);
        }


        private static void RunCRUD_Nhibernate(NhCustomerRepository customerRepository)
        {
            // Creating Employee and Customer with 2 Orders served but Employee
            var employee = new NhEmployee()
            {
                FirstName = "FirstName",
                Address = "Address",
                City = "City",
                LastName = "LastName",
                Title = "SuperEmployee"
            };

            var order1 = new NhOrder() { OrderID = 9001, Employee = employee, OrderDate = DateTime.Now };
            var order2 = new NhOrder() { OrderID = 9002, Employee = employee, OrderDate = DateTime.Now };

            var customer = new NhCustomer()
            {
                CustomerID = "TEST",
                City = "City",
                CompanyName = "CompanyName",
                ContactName = "ContactName",
                ContactTitle = "SuperCustomer",
                Orders = new List<NhOrder>() { order1, order2 }
            };

            // Execute save
            var savedCustomer = customerRepository.Save(customer);
            var customerId = savedCustomer.CustomerID;

            // Execute fetch
            var fetchedCustomer = customerRepository.FetchCustomerByID_Parameter(customerId);

            // will thow exception if those are different
            VerifyCustomersAreTheSameFromInside(savedCustomer, fetchedCustomer);

            // Execute delete
            customerRepository.Delete(fetchedCustomer);

            // try to fetch the same customer after delete and get nothing 
            var fetchingDeletedCustomer = customerRepository.FetchCustomerByID_Parameter(customerId);

            VerifyCustomersAreTheSameFromInside(fetchingDeletedCustomer, null);
        }

        private static void VerifyCustomersAreTheSameFromInside(NhCustomer customer1, NhCustomer customer2)
        {
            // we are not verifying everything here, just basic things..

            if (ReferenceEquals(customer1, customer2))
            {
                return;
            }

            if (customer1.CustomerID != customer2.CustomerID)
            {
                throw new Exception("CustomerID are different");
            }

            if (customer1.Orders.Count != customer2.Orders.Count)
            {
                throw new Exception("Orders count is different");
            }

            for (int i = 0; i < customer1.Orders.Count; i++)
            {
                if (customer1.Orders[i].OrderID != customer2.Orders[i].OrderID)
                {
                    throw new Exception("Order ID is different");
                }

                if (customer1.Orders[i].Employee.EmployeeID != customer2.Orders[i].Employee.EmployeeID)
                {
                    throw new Exception("EmployeeID is different in order");
                }

                if (customer1.Orders[i].CustomerID != customer2.Orders[i].CustomerID)
                {
                    throw new Exception("CustomerID is different in orders");
                }
            }

        }

        private static void RunCRUD(CustomerRepository customerRepository)
        {
            // Creating Employee and Customer with 2 Orders served but Employee
            var employee = new Employee()
            {
                FirstName = "FirstName",
                Address = "Address",
                City = "City",
                LastName = "LastName",
                Title = "SuperEmployee"
            };

            var order1 = new Order() { OrderID = 9001, Employee = employee, OrderDate = DateTime.Now };
            var order2 = new Order() { OrderID = 9002, Employee = employee, OrderDate = DateTime.Now };

            var customer = new Customer()
            {
                CustomerID = "TEST",
                City = "City",
                CompanyName = "CompanyName",
                ContactName = "ContactName",
                ContactTitle = "SuperCustomer",
                Orders = new List<Order>() { order1, order2 }
            };

            // Execute save
            var savedCustomer = customerRepository.Save(customer);
            var customerId = savedCustomer.CustomerID;

            // Execute fetch
            var fetchedCustomer = customerRepository.FetchCustomerByID(customerId);

            // will thow exception if those are different
            VerifyCustomersAreTheSameFromInside(savedCustomer, fetchedCustomer);

            // Execute delete
            customerRepository.Delete(fetchedCustomer);

            // try to fetch the same customer after delete and get nothing 
            var fetchingDeletedCustomer = customerRepository.FetchCustomerByID(customerId);

            VerifyCustomersAreTheSameFromInside(fetchingDeletedCustomer, null);
        }

        private static void VerifyCustomersAreTheSameFromInside(Customer customer1, Customer customer2)
        {
            // we are not verifying everything here, just basic things..

            if (ReferenceEquals(customer1, customer2))
            {
                return;
            }

            if (customer1.CustomerID != customer2.CustomerID)
            {
                throw new Exception("CustomerID are different");
            }

            if (customer1.Orders.Count != customer2.Orders.Count)
            {
                throw new Exception("Orders count is different");
            }

            for (int i = 0; i < customer1.Orders.Count; i++)
            {
                if (customer1.Orders[i].OrderID != customer2.Orders[i].OrderID)
                {
                    throw new Exception("Order ID is different");
                }

                if (customer1.Orders[i].Employee.EmployeeID != customer2.Orders[i].Employee.EmployeeID)
                {
                    throw new Exception("EmployeeID is different in order");
                }

                if (customer1.Orders[i].CustomerID != customer2.Orders[i].CustomerID)
                {
                    throw new Exception("CustomerID is different in orders");
                }
            }

        }
    }
}